home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Utilities / Programming / EnterAct 3.5 / hAWK project / AWK Source / AWK.Y < prev    next >
Encoding:
Text File  |  1991-12-03  |  36.4 KB  |  1,686 lines  |  [TEXT/TOPC]

  1. /*
  2.  * awk.y --- yacc/bison parser
  3.  */
  4.  
  5. /* Copyright © 1986, 1988, 1989 1991 the Free Software Foundation, Inc.
  6.  *         This file is part of GAWK, the GNU implementation of the
  7.  * AWK Progamming Language, modified for the Macintosh (also called hAWK).
  8.  *         GAWK is free software; you can redistribute or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; either version 1, or any later version.
  11.  *         GAWK is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14.  * GNU General Public License for more details.
  15.  *         You should have received a copy of the GNU General Public License
  16.  * along with GAWK; see the file "COPYING hAWK". If not, write to
  17.  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  */
  19. /* Mac note, this file not changed - instead, the output from this,
  20. "AWKTAB.C", has been directly modified (1/2 dozen functions added)
  21. */
  22.  
  23. %{
  24. #ifdef DEBUG
  25. #define YYDEBUG 12
  26. #endif
  27.  
  28. #include "awk.h"
  29.  
  30. /*
  31.  * This line is necessary since the Bison parser skeleton uses bcopy.
  32.  * Systems without memcpy should use -DMEMCPY_MISSING, per the Makefile.
  33.  * It should not hurt anything if Yacc is being used instead of Bison.
  34.  */
  35. #define bcopy(s,d,n)    memcpy((d),(s),(n))
  36.  
  37. extern void msg();
  38. extern struct re_pattern_buffer *mk_re_parse();
  39.  
  40. NODE *node();
  41. NODE *lookup();
  42. NODE *install();
  43.  
  44. static NODE *snode();
  45. static NODE *mkrangenode();
  46. static FILE *pathopen();
  47. static NODE *make_for_loop();
  48. static NODE *append_right();
  49. static void func_install();
  50. static NODE *make_param();
  51. static int hashf();
  52. static void pop_params();
  53. static void pop_var();
  54. static int yylex ();
  55. static void yyerror();
  56.  
  57. static int want_regexp;        /* lexical scanning kludge */
  58. static int want_assign;        /* lexical scanning kludge */
  59. static int can_return;        /* lexical scanning kludge */
  60. static int io_allowed = 1;    /* lexical scanning kludge */
  61. static int lineno = 1;        /* for error msgs */
  62. static char *lexptr;        /* pointer to next char during parsing */
  63. static char *lexptr_begin;    /* keep track of where we were for error msgs */
  64. static int curinfile = -1;    /* index into sourcefiles[] */
  65. static int param_counter;
  66.  
  67. NODE *variables[HASHSIZE];
  68.  
  69. extern int errcount;
  70. extern NODE *begin_block;
  71. extern NODE *end_block;
  72. /* expression_value is declared in AWK.H */
  73. %}
  74.  
  75. %union {
  76.     long lval;
  77.     AWKNUM fval;
  78.     NODE *nodeval;
  79.     NODETYPE nodetypeval;
  80.     char *sval;
  81.     NODE *(*ptrval)();
  82. }
  83.  
  84. %type <nodeval> function_prologue function_body
  85. %type <nodeval> rexp exp start program rule simp_exp
  86. %type <nodeval> pattern 
  87. %type <nodeval>    action variable param_list
  88. %type <nodeval>    rexpression_list opt_rexpression_list
  89. %type <nodeval>    expression_list opt_expression_list
  90. %type <nodeval>    statements statement if_statement opt_param_list 
  91. %type <nodeval> opt_exp opt_variable regexp 
  92. %type <nodeval> input_redir output_redir
  93. %type <nodetypeval> r_paren comma nls opt_nls print
  94.  
  95. %type <sval> func_name
  96. %token <sval> FUNC_CALL NAME REGEXP
  97. %token <lval> ERROR
  98. %token <nodeval> NUMBER YSTRING
  99. %token <nodetypeval> RELOP APPEND_OP
  100. %token <nodetypeval> ASSIGNOP MATCHOP NEWLINE CONCAT_OP
  101. %token <nodetypeval> LEX_BEGIN LEX_END LEX_IF LEX_ELSE LEX_RETURN LEX_DELETE
  102. %token <nodetypeval> LEX_WHILE LEX_DO LEX_FOR LEX_BREAK LEX_CONTINUE
  103. %token <nodetypeval> LEX_PRINT LEX_PRINTF LEX_NEXT LEX_EXIT LEX_FUNCTION
  104. %token <nodetypeval> LEX_GETLINE
  105. %token <nodetypeval> LEX_IN
  106. %token <lval> LEX_AND LEX_OR INCREMENT DECREMENT
  107. %token <ptrval> LEX_BUILTIN LEX_LENGTH
  108.  
  109. /* these are just yylval numbers */
  110.  
  111. /* Lowest to highest */
  112. %right ASSIGNOP
  113. %right '?' ':'
  114. %left LEX_OR
  115. %left LEX_AND
  116. %left LEX_GETLINE
  117. %nonassoc LEX_IN
  118. %left FUNC_CALL LEX_BUILTIN LEX_LENGTH
  119. %nonassoc MATCHOP
  120. %nonassoc RELOP '<' '>' '|' APPEND_OP
  121. %left CONCAT_OP
  122. %left YSTRING NUMBER
  123. %left '+' '-'
  124. %left '*' '/' '%'
  125. %right '!' UNARY
  126. %right '^'
  127. %left INCREMENT DECREMENT
  128. %left '$'
  129. %left '(' ')'
  130.  
  131. %%
  132.  
  133. start
  134.     : opt_nls program opt_nls
  135.         { expression_value = $2; }
  136.     ;
  137.  
  138. program
  139.     : rule
  140.         { 
  141.             if ($1 != NULL)
  142.                 $$ = $1;
  143.             else
  144.                 $$ = NULL;
  145.             yyerrok;
  146.         }
  147.     | program rule
  148.         /* add the rule to the tail of list */
  149.         {
  150.             if ($2 == NULL)
  151.                 $$ = $1;
  152.             else if ($1 == NULL)
  153.                 $$ = $2;
  154.             else {
  155.                 if ($1->type != Node_rule_list)
  156.                     $1 = node($1, Node_rule_list,
  157.                         (NODE*)NULL);
  158.                 $$ = append_right ($1,
  159.                    node($2, Node_rule_list,(NODE *) NULL));
  160.             }
  161.             yyerrok;
  162.         }
  163.     | error    { $$ = NULL; }
  164.     | program error { $$ = NULL; }
  165.     ;
  166.  
  167. rule
  168.     : LEX_BEGIN { io_allowed = 0; }
  169.       action
  170.       {
  171.         if (begin_block) {
  172.             if (begin_block->type != Node_rule_list)
  173.                 begin_block = node(begin_block, Node_rule_list,
  174.                     (NODE *)NULL);
  175.             append_right (begin_block, node(
  176.                 node((NODE *)NULL, Node_rule_node, $3),
  177.                 Node_rule_list, (NODE *)NULL) );
  178.         } else
  179.             begin_block = node((NODE *)NULL, Node_rule_node, $3);
  180.         $$ = NULL;
  181.         io_allowed = 1;
  182.         yyerrok;
  183.       }
  184.     | LEX_END { io_allowed = 0; }
  185.       action
  186.       {
  187.         if (end_block) {
  188.             if (end_block->type != Node_rule_list)
  189.                 end_block = node(end_block, Node_rule_list,
  190.                     (NODE *)NULL);
  191.             append_right (end_block, node(
  192.                 node((NODE *)NULL, Node_rule_node, $3),
  193.                 Node_rule_list, (NODE *)NULL));
  194.         } else
  195.             end_block = node((NODE *)NULL, Node_rule_node, $3);
  196.         $$ = NULL;
  197.         io_allowed = 1;
  198.         yyerrok;
  199.       }
  200.     | LEX_BEGIN statement_term
  201.       {
  202.         msg ("error near line %d: BEGIN blocks must have an action part", lineno);
  203.         errcount++;
  204.         yyerrok;
  205.       }
  206.     | LEX_END statement_term
  207.       {
  208.         msg ("error near line %d: END blocks must have an action part", lineno);
  209.         errcount++;
  210.         yyerrok;
  211.       }
  212.     | pattern action
  213.         { $$ = node ($1, Node_rule_node, $2); yyerrok; }
  214.     | action
  215.         { $$ = node ((NODE *)NULL, Node_rule_node, $1); yyerrok; }
  216.     | pattern statement_term
  217.         { if($1) $$ = node ($1, Node_rule_node, (NODE *)NULL); yyerrok; }
  218.     | function_prologue function_body
  219.         {
  220.             func_install($1, $2);
  221.             $$ = NULL;
  222.             yyerrok;
  223.         }
  224.     ;
  225.  
  226. func_name
  227.     : NAME
  228.         { $$ = $1; }
  229.     | FUNC_CALL
  230.         { $$ = $1; }
  231.     ;
  232.         
  233. function_prologue
  234.     : LEX_FUNCTION 
  235.         {
  236.             param_counter = 0;
  237.         }
  238.       func_name '(' opt_param_list r_paren opt_nls
  239.         {
  240.             $$ = append_right(make_param($3), $5);
  241.             can_return = 1;
  242.         }
  243.     ;
  244.  
  245. function_body
  246.     : l_brace statements r_brace
  247.       {
  248.         $$ = $2;
  249.         can_return = 0;
  250.       }
  251.     ;
  252.  
  253.  
  254. pattern
  255.     : exp
  256.         { $$ = $1; }
  257.     | exp comma exp
  258.         { $$ = mkrangenode ( node($1, Node_cond_pair, $3) ); }
  259.     ;
  260.  
  261. regexp
  262.     /*
  263.      * In this rule, want_regexp tells yylex that the next thing
  264.      * is a regexp so it should read up to the closing slash.
  265.      */
  266.     : '/'
  267.         { ++want_regexp; }
  268.        REGEXP '/'
  269.         {
  270.           want_regexp = 0;
  271.           $$ = node((NODE *)NULL,Node_regex,(NODE *)mk_re_parse($3, 0));
  272.           $$ -> re_case = 0;
  273.           emalloc ($$ -> re_text, char *, strlen($3)+1, "regexp");
  274.           strcpy ($$ -> re_text, $3);
  275.         }
  276.     ;
  277.  
  278. action
  279.     : l_brace r_brace opt_semi
  280.         {
  281.             /* empty actions are different from missing actions */
  282.             $$ = node ((NODE *) NULL, Node_illegal, (NODE *) NULL);
  283.         }
  284.     | l_brace statements r_brace opt_semi
  285.         { $$ = $2 ; }
  286.     ;
  287.  
  288. statements
  289.     : statement
  290.         { $$ = $1; }
  291.     | statements statement
  292.         {
  293.             if ($1 == NULL || $1->type != Node_statement_list)
  294.                 $1 = node($1, Node_statement_list,(NODE *)NULL);
  295.                 $$ = append_right($1,
  296.                 node( $2, Node_statement_list, (NODE *)NULL));
  297.                 yyerrok;
  298.         }
  299.     | error
  300.         { $$ = NULL; }
  301.     | statements error
  302.         { $$ = NULL; }
  303.     ;
  304.  
  305. statement_term
  306.     : nls
  307.         { $<nodetypeval>$ = Node_illegal; }
  308.     | semi opt_nls
  309.         { $<nodetypeval>$ = Node_illegal; }
  310.     ;
  311.  
  312.     
  313. statement
  314.     : semi opt_nls
  315.         { $$ = NULL; }
  316.     | l_brace r_brace
  317.         { $$ = NULL; }
  318.     | l_brace statements r_brace
  319.         { $$ = $2; }
  320.     | if_statement
  321.         { $$ = $1; }
  322.     | LEX_WHILE '(' exp r_paren opt_nls statement
  323.         { $$ = node ($3, Node_K_while, $6); }
  324.     | LEX_DO opt_nls statement LEX_WHILE '(' exp r_paren opt_nls
  325.         { $$ = node ($6, Node_K_do, $3); }
  326.     | LEX_FOR '(' NAME LEX_IN NAME r_paren opt_nls statement
  327.       {
  328.         $$ = node ($8, Node_K_arrayfor, make_for_loop(variable($3),
  329.             (NODE *)NULL, variable($5)));
  330.       }
  331.     | LEX_FOR '(' opt_exp semi exp semi opt_exp r_paren opt_nls statement
  332.       {
  333.         $$ = node($10, Node_K_for, (NODE *)make_for_loop($3, $5, $7));
  334.       }
  335.     | LEX_FOR '(' opt_exp semi semi opt_exp r_paren opt_nls statement
  336.       {
  337.         $$ = node ($9, Node_K_for,
  338.             (NODE *)make_for_loop($3, (NODE *)NULL, $6));
  339.       }
  340.     | LEX_BREAK statement_term
  341.        /* for break, maybe we'll have to remember where to break to */
  342.         { $$ = node ((NODE *)NULL, Node_K_break, (NODE *)NULL); }
  343.     | LEX_CONTINUE statement_term
  344.        /* similarly */
  345.         { $$ = node ((NODE *)NULL, Node_K_continue, (NODE *)NULL); }
  346.     | print '(' expression_list r_paren output_redir statement_term
  347.         { $$ = node ($3, $1, $5); }
  348.     | print opt_rexpression_list output_redir statement_term
  349.         { $$ = node ($2, $1, $3); }
  350.     | LEX_NEXT
  351.         { if (! io_allowed) yyerror("next used in BEGIN or END action"); }
  352.       statement_term
  353.         { $$ = node ((NODE *)NULL, Node_K_next, (NODE *)NULL); }
  354.     | LEX_EXIT opt_exp statement_term
  355.         { $$ = node ($2, Node_K_exit, (NODE *)NULL); }
  356.     | LEX_RETURN
  357.         { if (! can_return) yyerror("return used outside function context"); }
  358.       opt_exp statement_term
  359.         { $$ = node ($3, Node_K_return, (NODE *)NULL); }
  360.     | LEX_DELETE NAME '[' expression_list ']' statement_term
  361.         { $$ = node (variable($2), Node_K_delete, $4); }
  362.     | exp statement_term
  363.         { $$ = $1; }
  364.     ;
  365.  
  366. print
  367.     : LEX_PRINT
  368.         { $$ = $1; }
  369.     | LEX_PRINTF
  370.         { $$ = $1; }
  371.     ;
  372.  
  373. if_statement
  374.     : LEX_IF '(' exp r_paren opt_nls statement
  375.       {
  376.         $$ = node($3, Node_K_if, 
  377.             node($6, Node_if_branches, (NODE *)NULL));
  378.       }
  379.     | LEX_IF '(' exp r_paren opt_nls statement
  380.          LEX_ELSE opt_nls statement
  381.         { $$ = node ($3, Node_K_if,
  382.                 node ($6, Node_if_branches, $9)); }
  383.     ;
  384.  
  385. nls
  386.     : NEWLINE
  387.         { $<nodetypeval>$ = NULL; }
  388.     | nls NEWLINE
  389.         { $<nodetypeval>$ = NULL; }
  390.     ;
  391.  
  392. opt_nls
  393.     : /* empty */
  394.         { $<nodetypeval>$ = NULL; }
  395.     | nls
  396.         { $<nodetypeval>$ = NULL; }
  397.     ;
  398.  
  399. input_redir
  400.     : /* empty */
  401.         { $$ = NULL; }
  402.     | '<' simp_exp
  403.         { $$ = node ($2, Node_redirect_input, (NODE *)NULL); }
  404.     ;
  405.  
  406. output_redir
  407.     : /* empty */
  408.         { $$ = NULL; }
  409.     | '>' exp
  410.         { $$ = node ($2, Node_redirect_output, (NODE *)NULL); }
  411.     | APPEND_OP exp
  412.         { $$ = node ($2, Node_redirect_append, (NODE *)NULL); }
  413.     | '|' exp
  414.         { $$ = node ($2, Node_redirect_pipe, (NODE *)NULL); }
  415.     ;
  416.  
  417. opt_param_list
  418.     : /* empty */
  419.         { $$ = NULL; }
  420.     | param_list
  421.         { $$ = $1; }
  422.     ;
  423.  
  424. param_list
  425.     : NAME
  426.         { $$ = make_param($1); }
  427.     | param_list comma NAME
  428.         { $$ = append_right($1, make_param($3)); yyerrok; }
  429.     | error
  430.         { $$ = NULL; }
  431.     | param_list error
  432.         { $$ = NULL; }
  433.     | param_list comma error
  434.         { $$ = NULL; }
  435.     ;
  436.  
  437. /* optional expression, as in for loop */
  438. opt_exp
  439.     : /* empty */
  440.         { $$ = NULL; }
  441.     | exp
  442.         { $$ = $1; }
  443.     ;
  444.  
  445. opt_rexpression_list
  446.     : /* empty */
  447.         { $$ = NULL; }
  448.     | rexpression_list
  449.         { $$ = $1; }
  450.     ;
  451.  
  452. rexpression_list
  453.     : rexp
  454.         { $$ = node ($1, Node_expression_list, (NODE *)NULL); }
  455.     | rexpression_list comma rexp
  456.       {
  457.         $$ = append_right($1,
  458.             node( $3, Node_expression_list, (NODE *)NULL));
  459.         yyerrok;
  460.       }
  461.     | error
  462.         { $$ = NULL; }
  463.     | rexpression_list error
  464.         { $$ = NULL; }
  465.     | rexpression_list error rexp
  466.         { $$ = NULL; }
  467.     | rexpression_list comma error
  468.         { $$ = NULL; }
  469.     ;
  470.  
  471. opt_expression_list
  472.     : /* empty */
  473.         { $$ = NULL; }
  474.     | expression_list
  475.         { $$ = $1; }
  476.     ;
  477.  
  478. expression_list
  479.     : exp
  480.         { $$ = node ($1, Node_expression_list, (NODE *)NULL); }
  481.     | expression_list comma exp
  482.         {
  483.             $$ = append_right($1,
  484.                 node( $3, Node_expression_list, (NODE *)NULL));
  485.             yyerrok;
  486.         }
  487.     | error
  488.         { $$ = NULL; }
  489.     | expression_list error
  490.         { $$ = NULL; }
  491.     | expression_list error exp
  492.         { $$ = NULL; }
  493.     | expression_list comma error
  494.         { $$ = NULL; }
  495.     ;
  496.  
  497. /* Expressions, not including the comma operator.  */
  498. exp    : variable ASSIGNOP
  499.         { want_assign = 0; }
  500.         exp
  501.         { $$ = node ($1, $2, $4); }
  502.     | '(' expression_list r_paren LEX_IN NAME
  503.         { $$ = node (variable($5), Node_in_array, $2); }
  504.     | exp '|' LEX_GETLINE opt_variable
  505.         {
  506.           $$ = node ($4, Node_K_getline,
  507.              node ($1, Node_redirect_pipein, (NODE *)NULL));
  508.         }
  509.     | LEX_GETLINE opt_variable input_redir
  510.         {
  511.           /* "too painful to do right" */
  512.           /*
  513.           if (! io_allowed && $3 == NULL)
  514.             yyerror("non-redirected getline illegal inside BEGIN or END action");
  515.           */
  516.           $$ = node ($2, Node_K_getline, $3);
  517.         }
  518.     | exp LEX_AND exp
  519.         { $$ = node ($1, Node_and, $3); }
  520.     | exp LEX_OR exp
  521.         { $$ = node ($1, Node_or, $3); }
  522.     | exp MATCHOP exp
  523.          { $$ = node ($1, $2, $3); }
  524.     | regexp
  525.         { $$ = $1; }
  526.     | '!' regexp %prec UNARY
  527.         { $$ = node((NODE *) NULL, Node_nomatch, $2); }
  528.     | exp LEX_IN NAME
  529.         { $$ = node (variable($3), Node_in_array, $1); }
  530.     | exp RELOP exp
  531.         { $$ = node ($1, $2, $3); }
  532.     | exp '<' exp
  533.         { $$ = node ($1, Node_less, $3); }
  534.     | exp '>' exp
  535.         { $$ = node ($1, Node_greater, $3); }
  536.     | exp '?' exp ':' exp
  537.         { $$ = node($1, Node_cond_exp, node($3, Node_if_branches, $5));}
  538.     | simp_exp
  539.         { $$ = $1; }
  540.     | exp exp %prec CONCAT_OP
  541.         { $$ = node ($1, Node_concat, $2); }
  542.     ;
  543.  
  544. rexp    
  545.     : variable ASSIGNOP
  546.         { want_assign = 0; }
  547.         rexp
  548.         { $$ = node ($1, $2, $4); }
  549.     | rexp LEX_AND rexp
  550.         { $$ = node ($1, Node_and, $3); }
  551.     | rexp LEX_OR rexp
  552.         { $$ = node ($1, Node_or, $3); }
  553.     | LEX_GETLINE opt_variable input_redir
  554.         {
  555.           /* "too painful to do right" */
  556.           /*
  557.           if (! io_allowed && $3 == NULL)
  558.             yyerror("non-redirected getline illegal inside BEGIN or END action");
  559.           */
  560.           $$ = node ($2, Node_K_getline, $3);
  561.         }
  562.     | regexp
  563.         { $$ = $1; } 
  564.     | '!' regexp %prec UNARY
  565.         { $$ = node((NODE *) NULL, Node_nomatch, $2); }
  566.     | rexp MATCHOP rexp
  567.          { $$ = node ($1, $2, $3); }
  568.     | rexp LEX_IN NAME
  569.         { $$ = node (variable($3), Node_in_array, $1); }
  570.     | rexp RELOP rexp
  571.         { $$ = node ($1, $2, $3); }
  572.     | rexp '?' rexp ':' rexp
  573.         { $$ = node($1, Node_cond_exp, node($3, Node_if_branches, $5));}
  574.     | simp_exp
  575.         { $$ = $1; }
  576.     | rexp rexp %prec CONCAT_OP
  577.         { $$ = node ($1, Node_concat, $2); }
  578.     ;
  579.  
  580. simp_exp
  581.     : '!' simp_exp %prec UNARY
  582.         { $$ = node ($2, Node_not,(NODE *) NULL); }
  583.     | '(' exp r_paren
  584.         { $$ = $2; }
  585.     | LEX_BUILTIN '(' opt_expression_list r_paren
  586.         { $$ = snode ($3, Node_builtin, $1); }
  587.     | LEX_LENGTH '(' opt_expression_list r_paren
  588.         { $$ = snode ($3, Node_builtin, $1); }
  589.     | LEX_LENGTH
  590.         { $$ = snode ((NODE *)NULL, Node_builtin, $1); }
  591.     | FUNC_CALL '(' opt_expression_list r_paren
  592.       {
  593.         $$ = node ($3, Node_func_call, make_string($1, strlen($1)));
  594.       }
  595.     | INCREMENT variable
  596.         { $$ = node ($2, Node_preincrement, (NODE *)NULL); }
  597.     | DECREMENT variable
  598.         { $$ = node ($2, Node_predecrement, (NODE *)NULL); }
  599.     | variable INCREMENT
  600.         { $$ = node ($1, Node_postincrement, (NODE *)NULL); }
  601.     | variable DECREMENT
  602.         { $$ = node ($1, Node_postdecrement, (NODE *)NULL); }
  603.     | variable
  604.         { $$ = $1; }
  605.     | NUMBER
  606.         { $$ = $1; }
  607.     | YSTRING
  608.         { $$ = $1; }
  609.  
  610.     /* Binary operators in order of decreasing precedence.  */
  611.     | simp_exp '^' simp_exp
  612.         { $$ = node ($1, Node_exp, $3); }
  613.     | simp_exp '*' simp_exp
  614.         { $$ = node ($1, Node_times, $3); }
  615.     | simp_exp '/' simp_exp
  616.         { $$ = node ($1, Node_quotient, $3); }
  617.     | simp_exp '%' simp_exp
  618.         { $$ = node ($1, Node_mod, $3); }
  619.     | simp_exp '+' simp_exp
  620.         { $$ = node ($1, Node_plus, $3); }
  621.     | simp_exp '-' simp_exp
  622.         { $$ = node ($1, Node_minus, $3); }
  623.     | '-' simp_exp    %prec UNARY
  624.         { $$ = node ($2, Node_unary_minus, (NODE *)NULL); }
  625.     | '+' simp_exp    %prec UNARY
  626.         { $$ = $2; }
  627.     ;
  628.  
  629. opt_variable
  630.     : /* empty */
  631.         { $$ = NULL; }
  632.     | variable
  633.         { $$ = $1; }
  634.     ;
  635.  
  636. variable
  637.     : NAME
  638.         { want_assign = 1; $$ = variable ($1); }
  639.     | NAME '[' expression_list ']'
  640.         { want_assign = 1; $$ = node (variable($1), Node_subscript, $3); }
  641.     | '$' simp_exp
  642.         { want_assign = 1; $$ = node ($2, Node_field_spec, (NODE *)NULL); }
  643.     ;
  644.  
  645. l_brace
  646.     : '{' opt_nls
  647.     ;
  648.  
  649. r_brace
  650.     : '}' opt_nls    { yyerrok; }
  651.     ;
  652.  
  653. r_paren
  654.     : ')' { $<nodetypeval>$ = Node_illegal; yyerrok; }
  655.     ;
  656.  
  657. opt_semi
  658.     : /* empty */
  659.     | semi
  660.     ;
  661.  
  662. semi
  663.     : ';'    { yyerrok; }
  664.     ;
  665.  
  666. comma    : ',' opt_nls    { $<nodetypeval>$ = Node_illegal; yyerrok; }
  667.     ;
  668.  
  669. %%
  670.  
  671. struct token {
  672.     char *operator;        /* text to match */
  673.     NODETYPE value;        /* node type */
  674.     int class;        /* lexical class */
  675.     short nostrict;        /* ignore if in strict compatibility mode */
  676.     NODE *(*ptr) ();    /* function that implements this keyword */
  677. };
  678.  
  679. extern NODE
  680.     *do_exp(),    *do_getline(),    *do_index(),    *do_length(),
  681.     *do_sqrt(),    *do_log(),    *do_sprintf(),    *do_substr(),
  682.     *do_split(),    *do_system(),    *do_int(),    *do_close(),
  683.     *do_atan2(),    *do_sin(),    *do_cos(),    *do_rand(),
  684.     *do_srand(),    *do_match(),    *do_tolower(),    *do_toupper(),
  685.     *do_sub(),    *do_gsub(), *do_lookup();
  686.  
  687. /* Special functions for debugging */
  688. #ifdef DEBUG
  689. NODE *do_prvars(), *do_bp();
  690. #endif
  691.  
  692. /* Tokentab is sorted ascii ascending order, so it can be binary searched. */
  693.  
  694. static struct token tokentab[] = {
  695.     { "BEGIN",    Node_illegal,        LEX_BEGIN,    0,    0 },
  696.     { "END",    Node_illegal,        LEX_END,    0,    0 },
  697.     { "atan2",    Node_builtin,        LEX_BUILTIN,    0,    do_atan2 },
  698. #ifdef DEBUG
  699.     { "bp",        Node_builtin,        LEX_BUILTIN,    0,    do_bp },
  700. #endif
  701.     { "break",    Node_K_break,        LEX_BREAK,    0,    0 },
  702.     { "close",    Node_builtin,        LEX_BUILTIN,    0,    do_close },
  703.     { "continue",    Node_K_continue,    LEX_CONTINUE,    0,    0 },
  704.     { "cos",    Node_builtin,        LEX_BUILTIN,    0,    do_cos },
  705.     { "delete",    Node_K_delete,        LEX_DELETE,    0,    0 },
  706.     { "do",        Node_K_do,        LEX_DO,        0,    0 },
  707.     { "else",    Node_illegal,        LEX_ELSE,    0,    0 },
  708.     { "exit",    Node_K_exit,        LEX_EXIT,    0,    0 },
  709.     { "exp",    Node_builtin,        LEX_BUILTIN,    0,    do_exp },
  710.     { "for",    Node_K_for,        LEX_FOR,    0,    0 },
  711.     { "func",    Node_K_function,    LEX_FUNCTION,    0,    0 },
  712.     { "function",    Node_K_function,    LEX_FUNCTION,    0,    0 },
  713.     { "getline",    Node_K_getline,        LEX_GETLINE,    0,    0 },
  714.     { "gsub",    Node_builtin,        LEX_BUILTIN,    0,    do_gsub },
  715.     { "if",        Node_K_if,        LEX_IF,        0,    0 },
  716.     { "in",        Node_illegal,        LEX_IN,        0,    0 },
  717.     { "index",    Node_builtin,        LEX_BUILTIN,    0,    do_index },
  718.     { "int",    Node_builtin,        LEX_BUILTIN,    0,    do_int },
  719.     { "length",    Node_builtin,        LEX_LENGTH,    0,    do_length },
  720.     { "log",    Node_builtin,        LEX_BUILTIN,    0,    do_log },
  721.     { "lookup",    Node_builtin,        LEX_LENGTH,    0,    do_lookup },
  722.     { "match",    Node_builtin,        LEX_BUILTIN,    0,    do_match },
  723.     { "next",    Node_K_next,        LEX_NEXT,    0,    0 },
  724.     { "print",    Node_K_print,        LEX_PRINT,    0,    0 },
  725.     { "printf",    Node_K_printf,        LEX_PRINTF,    0,    0 },
  726. #ifdef DEBUG
  727.     { "prvars",    Node_builtin,        LEX_BUILTIN,    0,    do_prvars },
  728. #endif
  729.     { "rand",    Node_builtin,        LEX_BUILTIN,    0,    do_rand },
  730.     { "return",    Node_K_return,        LEX_RETURN,    0,    0 },
  731.     { "sin",    Node_builtin,        LEX_BUILTIN,    0,    do_sin },
  732.     { "split",    Node_builtin,        LEX_BUILTIN,    0,    do_split },
  733.     { "sprintf",    Node_builtin,        LEX_BUILTIN,    0,    do_sprintf },
  734.     { "sqrt",    Node_builtin,        LEX_BUILTIN,    0,    do_sqrt },
  735.     { "srand",    Node_builtin,        LEX_BUILTIN,    0,    do_srand },
  736.     { "sub",    Node_builtin,        LEX_BUILTIN,    0,    do_sub },
  737.     { "substr",    Node_builtin,        LEX_BUILTIN,    0,    do_substr },
  738.     { "system",    Node_builtin,        LEX_BUILTIN,    0,    do_system },
  739.     { "tolower",    Node_builtin,        LEX_BUILTIN,    0,    do_tolower },
  740.     { "toupper",    Node_builtin,        LEX_BUILTIN,    0,    do_toupper },
  741.     { "while",    Node_K_while,        LEX_WHILE,    0,    0 },
  742. };
  743.  
  744. static char *token_start;
  745.  
  746. /* VARARGS0 */
  747. static void
  748. yyerror(va_alist)
  749. va_dcl
  750. {
  751.     va_list args;
  752.     char *mesg;
  753.     register char *ptr, *beg;
  754.     char *scan;
  755.  
  756.     errcount++;
  757.     /* Find the current line in the input file */
  758.     if (! lexptr) {
  759.         beg = "(END OF FILE)";
  760.         ptr = beg + 13;
  761.     } else {
  762.         if (*lexptr == '\n' && lexptr != lexptr_begin)
  763.             --lexptr;
  764.         for (beg = lexptr; beg != lexptr_begin && *beg != '\n'; --beg)
  765.             ;
  766.         /* NL isn't guaranteed */
  767.         for (ptr = lexptr; *ptr && *ptr != '\n'; ptr++)
  768.             ;
  769.         if (beg != lexptr_begin)
  770.             beg++;
  771.     }
  772.     msg("syntax error near line %d:\n%.*s", lineno, ptr - beg, beg);
  773.     scan = beg;
  774.     while (scan < token_start)
  775.         if (*scan++ == '\t')
  776.             putc('\t', stderr);
  777.         else
  778.             putc(' ', stderr);
  779.     putc('^', stderr);
  780.     putc(' ', stderr);
  781.     va_start(args);
  782.     mesg = va_arg(args, char *);
  783.     vfprintf(stderr, mesg, args);
  784.     va_end(args);
  785.     putc('\n', stderr);
  786.     exit(1);
  787. }
  788.  
  789. /*
  790.  * Parse a C escape sequence. STRING_PTR points to a variable containing a
  791.  * pointer to the string to parse. That pointer is updated past the
  792.  characters we use. The value of the escape sequence is returned.
  793.  
  794.  * A negative value means the sequence \ newline was seen, which is
  795.  supposed to be equivalent to nothing at all.
  796.  
  797.  * If \ is followed by a null character, we return a negative value and
  798.  leave the string pointer pointing at the null character.
  799.  
  800.  * If \ is followed by 000, we return 0 and leave the string pointer after
  801.  the zeros. A value of 0 does not mean end of string.  
  802.  */
  803.  
  804. int
  805. parse_escape(string_ptr)
  806. char **string_ptr;
  807. {
  808.     register int c = *(*string_ptr)++;
  809.     register int i;
  810.     register int count;
  811.  
  812.     switch (c) {
  813.     case 'a':
  814.         return BELL;
  815.     case 'b':
  816.         return '\b';
  817.     case 'f':
  818.         return '\f';
  819.     case 'n':
  820.         return '\n';
  821.     case 'r':
  822.         return '\r';
  823.     case 't':
  824.         return '\t';
  825.     case 'v':
  826.         return '\v';
  827.     case '\n':
  828.         return -2;
  829.     case 0:
  830.         (*string_ptr)--;
  831.         return -1;
  832.     case '0':
  833.     case '1':
  834.     case '2':
  835.     case '3':
  836.     case '4':
  837.     case '5':
  838.     case '6':
  839.     case '7':
  840.         i = c - '0';
  841.         count = 0;
  842.         while (++count < 3) {
  843.             if ((c = *(*string_ptr)++) >= '0' && c <= '7') {
  844.                 i *= 8;
  845.                 i += c - '0';
  846.             } else {
  847.                 (*string_ptr)--;
  848.                 break;
  849.             }
  850.         }
  851.         return i;
  852.     case 'x':
  853.         i = 0;
  854.         while (1) {
  855.             if (isxdigit((c = *(*string_ptr)++))) {
  856.                 if (isdigit(c))
  857.                     i += c - '0';
  858.                 else if (isupper(c))
  859.                     i += c - 'A' + 10;
  860.                 else
  861.                     i += c - 'a' + 10;
  862.             } else {
  863.                 (*string_ptr)--;
  864.                 break;
  865.             }
  866.         }
  867.         return i;
  868.     default:
  869.         return c;
  870.     }
  871. }
  872.  
  873. /*
  874.  * Read the input and turn it into tokens. Input is now read from a file
  875.  * instead of from malloc'ed memory. The main program takes a program
  876.  * passed as a command line argument and writes it to a temp file. Otherwise
  877.  * the file name is made available in an external variable.
  878.  */
  879.  
  880. static int
  881. yylex()
  882. {
  883.     register int c;
  884.     register int namelen;
  885.     register char *tokstart;
  886.     char *tokkey;
  887.     static did_newline = 0;    /* the grammar insists that actions end
  888.                  * with newlines.  This was easier than
  889.                  * hacking the grammar. */
  890.     int seen_e = 0;        /* These are for numbers */
  891.     int seen_point = 0;
  892.     int esc_seen;
  893.     extern char **sourcefile;
  894.     extern int tempsource, numfiles;
  895.     static int file_opened = 0;
  896.     static FILE *fin;
  897.     static char cbuf[BUFSIZ];
  898.     int low, mid, high;
  899. #ifdef DEBUG
  900.     extern int debugging;
  901. #endif
  902.  
  903.     if (! file_opened) {
  904.         file_opened = 1;
  905. #ifdef DEBUG
  906.         if (debugging) {
  907.             int i;
  908.  
  909.             for (i = 0; i <= numfiles; i++)
  910.                 fprintf (stderr, "sourcefile[%d] = %s\n", i,
  911.                         sourcefile[i]);
  912.         }
  913. #endif
  914.     nextfile:
  915.         if ((fin = pathopen (sourcefile[++curinfile])) == NULL)
  916.             fatal("cannot open `%s' for reading (%s)",
  917.                 sourcefile[curinfile],
  918.                 strerror(errno));
  919.         *(lexptr = cbuf) = '\0';
  920.         /*
  921.          * immediately unlink the tempfile so that it will
  922.          * go away cleanly if we bomb.
  923.          */
  924.         if (tempsource && curinfile == 0)
  925.             (void) unlink (sourcefile[curinfile]);
  926.     }
  927.  
  928. retry:
  929.     if (! *lexptr)
  930.         if (fgets (cbuf, sizeof cbuf, fin) == NULL) {
  931.             if (fin != NULL)
  932.                 fclose (fin);    /* be neat and clean */
  933.             if (curinfile < numfiles)
  934.                 goto nextfile;
  935.             return 0;
  936.         } else
  937.             lexptr = lexptr_begin = cbuf;
  938.  
  939.     if (want_regexp) {
  940.         int in_brack = 0;
  941.  
  942.         want_regexp = 0;
  943.         token_start = tokstart = lexptr;
  944.         while (c = *lexptr++) {
  945.             switch (c) {
  946.             case '[':
  947.                 in_brack = 1;
  948.                 break;
  949.             case ']':
  950.                 in_brack = 0;
  951.                 break;
  952.             case '\\':
  953.                 if (*lexptr++ == '\0') {
  954.                     yyerror("unterminated regexp ends with \\");
  955.                     return ERROR;
  956.                 } else if (lexptr[-1] == '\n')
  957.                     goto retry;
  958.                 break;
  959.             case '/':    /* end of the regexp */
  960.                 if (in_brack)
  961.                     break;
  962.  
  963.                 lexptr--;
  964.                 yylval.sval = tokstart;
  965.                 return REGEXP;
  966.             case '\n':
  967.                 lineno++;
  968.             case '\0':
  969.                 lexptr--;    /* so error messages work */
  970.                 yyerror("unterminated regexp");
  971.                 return ERROR;
  972.             }
  973.         }
  974.     }
  975.  
  976.     if (*lexptr == '\n') {
  977.         lexptr++;
  978.         lineno++;
  979.         return NEWLINE;
  980.     }
  981.  
  982.     while (*lexptr == ' ' || *lexptr == '\t')
  983.         lexptr++;
  984.  
  985.     token_start = tokstart = lexptr;
  986.  
  987.     switch (c = *lexptr++) {
  988.     case 0:
  989.         return 0;
  990.  
  991.     case '\n':
  992.         lineno++;
  993.         return NEWLINE;
  994.  
  995.     case '#':        /* it's a comment */
  996.         while (*lexptr != '\n' && *lexptr != '\0')
  997.             lexptr++;
  998.         goto retry;
  999.  
  1000.     case '\\':
  1001.         if (*lexptr == '\n') {
  1002.             lineno++;
  1003.             lexptr++;
  1004.             goto retry;
  1005.         } else
  1006.             break;
  1007.     case ')':
  1008.     case ']':
  1009.     case '(':    
  1010.     case '[':
  1011.     case '$':
  1012.     case ';':
  1013.     case ':':
  1014.     case '?':
  1015.  
  1016.         /*
  1017.          * set node type to ILLEGAL because the action should set it
  1018.          * to the right thing 
  1019.          */
  1020.         yylval.nodetypeval = Node_illegal;
  1021.         return c;
  1022.  
  1023.     case '{':
  1024.     case ',':
  1025.         yylval.nodetypeval = Node_illegal;
  1026.         return c;
  1027.  
  1028.     case '*':
  1029.         if (*lexptr == '=') {
  1030.             yylval.nodetypeval = Node_assign_times;
  1031.             lexptr++;
  1032.             return ASSIGNOP;
  1033.         } else if (*lexptr == '*') {    /* make ** and **= aliases
  1034.                          * for ^ and ^= */
  1035.             if (lexptr[1] == '=') {
  1036.                 yylval.nodetypeval = Node_assign_exp;
  1037.                 lexptr += 2;
  1038.                 return ASSIGNOP;
  1039.             } else {
  1040.                 yylval.nodetypeval = Node_illegal;
  1041.                 lexptr++;
  1042.                 return '^';
  1043.             }
  1044.         }
  1045.         yylval.nodetypeval = Node_illegal;
  1046.         return c;
  1047.  
  1048.     case '/':
  1049.         if (want_assign && *lexptr == '=') {
  1050.             yylval.nodetypeval = Node_assign_quotient;
  1051.             lexptr++;
  1052.             return ASSIGNOP;
  1053.         }
  1054.         yylval.nodetypeval = Node_illegal;
  1055.         return c;
  1056.  
  1057.     case '%':
  1058.         if (*lexptr == '=') {
  1059.             yylval.nodetypeval = Node_assign_mod;
  1060.             lexptr++;
  1061.             return ASSIGNOP;
  1062.         }
  1063.         yylval.nodetypeval = Node_illegal;
  1064.         return c;
  1065.  
  1066.     case '^':
  1067.         if (*lexptr == '=') {
  1068.             yylval.nodetypeval = Node_assign_exp;
  1069.             lexptr++;
  1070.             return ASSIGNOP;
  1071.         }
  1072.         yylval.nodetypeval = Node_illegal;
  1073.         return c;
  1074.  
  1075.     case '+':
  1076.         if (*lexptr == '=') {
  1077.             yylval.nodetypeval = Node_assign_plus;
  1078.             lexptr++;
  1079.             return ASSIGNOP;
  1080.         }
  1081.         if (*lexptr == '+') {
  1082.             yylval.nodetypeval = Node_illegal;
  1083.             lexptr++;
  1084.             return INCREMENT;
  1085.         }
  1086.         yylval.nodetypeval = Node_illegal;
  1087.         return c;
  1088.  
  1089.     case '!':
  1090.         if (*lexptr == '=') {
  1091.             yylval.nodetypeval = Node_notequal;
  1092.             lexptr++;
  1093.             return RELOP;
  1094.         }
  1095.         if (*lexptr == '~') {
  1096.             yylval.nodetypeval = Node_nomatch;
  1097.             lexptr++;
  1098.             return MATCHOP;
  1099.         }
  1100.         yylval.nodetypeval = Node_illegal;
  1101.         return c;
  1102.  
  1103.     case '<':
  1104.         if (*lexptr == '=') {
  1105.             yylval.nodetypeval = Node_leq;
  1106.             lexptr++;
  1107.             return RELOP;
  1108.         }
  1109.         yylval.nodetypeval = Node_less;
  1110.         return c;
  1111.  
  1112.     case '=':
  1113.         if (*lexptr == '=') {
  1114.             yylval.nodetypeval = Node_equal;
  1115.             lexptr++;
  1116.             return RELOP;
  1117.         }
  1118.         yylval.nodetypeval = Node_assign;
  1119.         return ASSIGNOP;
  1120.  
  1121.     case '>':
  1122.         if (*lexptr == '=') {
  1123.             yylval.nodetypeval = Node_geq;
  1124.             lexptr++;
  1125.             return RELOP;
  1126.         } else if (*lexptr == '>') {
  1127.             yylval.nodetypeval = Node_redirect_append;
  1128.             lexptr++;
  1129.             return APPEND_OP;
  1130.         }
  1131.         yylval.nodetypeval = Node_greater;
  1132.         return c;
  1133.  
  1134.     case '~':
  1135.         yylval.nodetypeval = Node_match;
  1136.         return MATCHOP;
  1137.  
  1138.     case '}':
  1139.         /*
  1140.          * Added did newline stuff.  Easier than
  1141.          * hacking the grammar
  1142.          */
  1143.         if (did_newline) {
  1144.             did_newline = 0;
  1145.             return c;
  1146.         }
  1147.         did_newline++;
  1148.         --lexptr;
  1149.         return NEWLINE;
  1150.  
  1151.     case '"':
  1152.         esc_seen = 0;
  1153.         while (*lexptr != '\0') {
  1154.             switch (*lexptr++) {
  1155.             case '\\':
  1156.                 esc_seen = 1;
  1157.                 if (*lexptr == '\n')
  1158.                     yyerror("newline in string");
  1159.                 if (*lexptr++ != '\0')
  1160.                     break;
  1161.                 /* fall through */
  1162.             case '\n':
  1163.                 lexptr--;
  1164.                 yyerror("unterminated string");
  1165.                 return ERROR;
  1166.             case '"':
  1167.                 yylval.nodeval = make_str_node(tokstart + 1,
  1168.                         lexptr-tokstart-2, esc_seen);
  1169.                 yylval.nodeval->flags |= PERM;
  1170.                 return YSTRING;
  1171.             }
  1172.         }
  1173.         return ERROR;
  1174.  
  1175.     case '-':
  1176.         if (*lexptr == '=') {
  1177.             yylval.nodetypeval = Node_assign_minus;
  1178.             lexptr++;
  1179.             return ASSIGNOP;
  1180.         }
  1181.         if (*lexptr == '-') {
  1182.             yylval.nodetypeval = Node_illegal;
  1183.             lexptr++;
  1184.             return DECREMENT;
  1185.         }
  1186.         yylval.nodetypeval = Node_illegal;
  1187.         return c;
  1188.  
  1189.     case '0':
  1190.     case '1':
  1191.     case '2':
  1192.     case '3':
  1193.     case '4':
  1194.     case '5':
  1195.     case '6':
  1196.     case '7':
  1197.     case '8':
  1198.     case '9':
  1199.     case '.':
  1200.         /* It's a number */
  1201.         for (namelen = 0; (c = tokstart[namelen]) != '\0'; namelen++) {
  1202.             switch (c) {
  1203.             case '.':
  1204.                 if (seen_point)
  1205.                     goto got_number;
  1206.                 ++seen_point;
  1207.                 break;
  1208.             case 'e':
  1209.             case 'E':
  1210.                 if (seen_e)
  1211.                     goto got_number;
  1212.                 ++seen_e;
  1213.                 if (tokstart[namelen + 1] == '-' ||
  1214.                     tokstart[namelen + 1] == '+')
  1215.                     namelen++;
  1216.                 break;
  1217.             case '0':
  1218.             case '1':
  1219.             case '2':
  1220.             case '3':
  1221.             case '4':
  1222.             case '5':
  1223.             case '6':
  1224.             case '7':
  1225.             case '8':
  1226.             case '9':
  1227.                 break;
  1228.             default:
  1229.                 goto got_number;
  1230.             }
  1231.         }
  1232.  
  1233. got_number:
  1234.         lexptr = tokstart + namelen;
  1235.         /*
  1236.         yylval.nodeval = make_string(tokstart, namelen);
  1237.         (void) force_number(yylval.nodeval);
  1238.         */
  1239.         yylval.nodeval = make_number(atof(tokstart));
  1240.         yylval.nodeval->flags |= PERM;
  1241.         return NUMBER;
  1242.  
  1243.     case '&':
  1244.         if (*lexptr == '&') {
  1245.             yylval.nodetypeval = Node_and;
  1246.             while (c = *++lexptr) {
  1247.                 if (c == '#')
  1248.                     while ((c = *++lexptr) != '\n'
  1249.                            && c != '\0')
  1250.                         ;
  1251.                 if (c == '\n')
  1252.                     lineno++;
  1253.                 else if (! isspace(c))
  1254.                     break;
  1255.             }
  1256.             return LEX_AND;
  1257.         }
  1258.         return ERROR;
  1259.  
  1260.     case '|':
  1261.         if (*lexptr == '|') {
  1262.             yylval.nodetypeval = Node_or;
  1263.             while (c = *++lexptr) {
  1264.                 if (c == '#')
  1265.                     while ((c = *++lexptr) != '\n'
  1266.                            && c != '\0')
  1267.                         ;
  1268.                 if (c == '\n')
  1269.                     lineno++;
  1270.                 else if (! isspace(c))
  1271.                     break;
  1272.             }
  1273.             return LEX_OR;
  1274.         }
  1275.         yylval.nodetypeval = Node_illegal;
  1276.         return c;
  1277.     }
  1278.  
  1279.     if (c != '_' && ! isalpha(c)) {
  1280.         yyerror("Invalid char '%c' in expression\n", c);
  1281.         return ERROR;
  1282.     }
  1283.  
  1284.     /* it's some type of name-type-thing.  Find its length */
  1285.     for (namelen = 0; is_identchar(tokstart[namelen]); namelen++)
  1286.         /* null */ ;
  1287.     emalloc(tokkey, char *, namelen+1, "yylex");
  1288.     memcpy(tokkey, tokstart, namelen);
  1289.     tokkey[namelen] = '\0';
  1290.  
  1291.     /* See if it is a special token.  */
  1292.     low = 0;
  1293.     high = (sizeof (tokentab) / sizeof (tokentab[0])) - 1;
  1294.     while (low <= high) {
  1295.         int i, c;
  1296.  
  1297.         mid = (low + high) / 2;
  1298.         c = *tokstart - tokentab[mid].operator[0];
  1299.         i = c ? c : strcmp (tokkey, tokentab[mid].operator);
  1300.  
  1301.         if (i < 0) {        /* token < mid */
  1302.             high = mid - 1;
  1303.         } else if (i > 0) {    /* token > mid */
  1304.             low = mid + 1;
  1305.         } else {
  1306.             lexptr = tokstart + namelen;
  1307.             if (strict && tokentab[mid].nostrict)
  1308.                 break;
  1309.             if (tokentab[mid].class == LEX_BUILTIN
  1310.                 || tokentab[mid].class == LEX_LENGTH)
  1311.                 yylval.ptrval = tokentab[mid].ptr;
  1312.             else
  1313.                 yylval.nodetypeval = tokentab[mid].value;
  1314.             return tokentab[mid].class;
  1315.         }
  1316.     }
  1317.  
  1318.     /* It's a name.  See how long it is.  */
  1319.     yylval.sval = tokkey;
  1320.     lexptr = tokstart + namelen;
  1321.     if (*lexptr == '(')
  1322.         return FUNC_CALL;
  1323.     else
  1324.         return NAME;
  1325. }
  1326.  
  1327. #ifndef DEFPATH
  1328. #ifdef MSDOS
  1329. #define DEFPATH    "."
  1330. #define ENVSEP    ';'
  1331. #else
  1332. #define DEFPATH    ".:/usr/lib/awk:/usr/local/lib/awk"
  1333. #define ENVSEP    ':'
  1334. #endif
  1335. #endif
  1336.  
  1337. static FILE *
  1338. pathopen (file)
  1339. char *file;
  1340. {
  1341.     static char *savepath = DEFPATH;
  1342.     static int first = 1;
  1343.     char *awkpath, *cp;
  1344.     char trypath[BUFSIZ];
  1345.     FILE *fp;
  1346. #ifdef DEBUG
  1347.     extern int debugging;
  1348. #endif
  1349.     int fd;
  1350.  
  1351.     if (strcmp (file, "-") == 0)
  1352.         return (stdin);
  1353.  
  1354.     if (strict)
  1355.         return (fopen (file, "r"));
  1356.  
  1357.     if (first) {
  1358.         first = 0;
  1359.         if ((awkpath = getenv ("AWKPATH")) != NULL && *awkpath)
  1360.             savepath = awkpath;    /* used for restarting */
  1361.     }
  1362.     awkpath = savepath;
  1363.  
  1364.     /* some kind of path name, no search */
  1365. #ifndef MSDOS
  1366.     if (strchr (file, '/') != NULL)
  1367. #else
  1368.     if (strchr (file, '/') != NULL || strchr (file, '\\') != NULL
  1369.             || strchr (file, ':') != NULL)
  1370. #endif
  1371.         return ( (fd = devopen (file, "r")) >= 0 ?
  1372.                 fdopen(fd, "r") :
  1373.                 NULL);
  1374.  
  1375.     do {
  1376.         trypath[0] = '\0';
  1377.         /* this should take into account limits on size of trypath */
  1378.         for (cp = trypath; *awkpath && *awkpath != ENVSEP; )
  1379.             *cp++ = *awkpath++;
  1380.  
  1381.         if (cp != trypath) {    /* nun-null element in path */
  1382.             *cp++ = '/';
  1383.             strcpy (cp, file);
  1384.         } else
  1385.             strcpy (trypath, file);
  1386. #ifdef DEBUG
  1387.         if (debugging)
  1388.             fprintf(stderr, "trying: %s\n", trypath);
  1389. #endif
  1390.         if ((fd = devopen (trypath, "r")) >= 0
  1391.             && (fp = fdopen(fd, "r")) != NULL)
  1392.             return (fp);
  1393.  
  1394.         /* no luck, keep going */
  1395.         if(*awkpath == ENVSEP && awkpath[1] != '\0')
  1396.             awkpath++;    /* skip colon */
  1397.     } while (*awkpath);
  1398. #ifdef MSDOS
  1399.     /*
  1400.      * Under DOS (and probably elsewhere) you might have one of the awk
  1401.      * paths defined, WITHOUT the current working directory in it.
  1402.      * Therefore you should try to open the file in the current directory.
  1403.      */
  1404.     return ( (fd = devopen(file, "r")) >= 0 ? fdopen(fd, "r") : NULL);
  1405. #else
  1406.     return (NULL);
  1407. #endif
  1408. }
  1409.  
  1410. static NODE *
  1411. node_common(op)
  1412. NODETYPE op;
  1413. {
  1414.     register NODE *r;
  1415.     extern int numfiles;
  1416.     extern int tempsource;
  1417.     extern char **sourcefile;
  1418.  
  1419.     r = newnode(op);
  1420.     r->source_line = lineno;
  1421.     if (numfiles > -1 && ! tempsource)
  1422.         r->source_file = sourcefile[curinfile];
  1423.     else
  1424.         r->source_file = NULL;
  1425.     return r;
  1426. }
  1427.  
  1428. /*
  1429.  * This allocates a node with defined lnode and rnode. 
  1430.  * This should only be used by yyparse+co while reading in the program 
  1431.  */
  1432. NODE *
  1433. node(left, op, right)
  1434. NODE *left, *right;
  1435. NODETYPE op;
  1436. {
  1437.     register NODE *r;
  1438.  
  1439.     r = node_common(op);
  1440.     r->lnode = left;
  1441.     r->rnode = right;
  1442.     return r;
  1443. }
  1444.  
  1445. /*
  1446.  * This allocates a node with defined subnode and proc
  1447.  * Otherwise like node()
  1448.  */
  1449. static NODE *
  1450. snode(subn, op, procp)
  1451. NODETYPE op;
  1452. NODE *(*procp) ();
  1453. NODE *subn;
  1454. {
  1455.     register NODE *r;
  1456.  
  1457.     r = node_common(op);
  1458.     r->subnode = subn;
  1459.     r->proc = procp;
  1460.     return r;
  1461. }
  1462.  
  1463. /*
  1464.  * This allocates a Node_line_range node with defined condpair and
  1465.  * zeroes the trigger word to avoid the temptation of assuming that calling
  1466.  * 'node( foo, Node_line_range, 0)' will properly initialize 'triggered'. 
  1467.  */
  1468. /* Otherwise like node() */
  1469. static NODE *
  1470. mkrangenode(cpair)
  1471. NODE *cpair;
  1472. {
  1473.     register NODE *r;
  1474.  
  1475.     r = newnode(Node_line_range);
  1476.     r->condpair = cpair;
  1477.     r->triggered = 0;
  1478.     return r;
  1479. }
  1480.  
  1481. /* Build a for loop */
  1482. static NODE *
  1483. make_for_loop(init, cond, incr)
  1484. NODE *init, *cond, *incr;
  1485. {
  1486.     register FOR_LOOP_HEADER *r;
  1487.     NODE *n;
  1488.  
  1489.     emalloc(r, FOR_LOOP_HEADER *, sizeof(FOR_LOOP_HEADER), "make_for_loop");
  1490.     n = newnode(Node_illegal);
  1491.     r->init = init;
  1492.     r->cond = cond;
  1493.     r->incr = incr;
  1494.     n->sub.nodep.r.hd = r;
  1495.     return n;
  1496. }
  1497.  
  1498. /*
  1499.  * Install a name in the hash table specified, even if it is already there.
  1500.  * Name stops with first non alphanumeric. Caller must check against
  1501.  * redefinition if that is desired. 
  1502.  */
  1503. NODE *
  1504. install(table, name, value)
  1505. NODE **table;
  1506. char *name;
  1507. NODE *value;
  1508. {
  1509.     register NODE *hp;
  1510.     register int len, bucket;
  1511.     register char *p;
  1512.  
  1513.     len = 0;
  1514.     p = name;
  1515.     while (is_identchar(*p))
  1516.         p++;
  1517.     len = p - name;
  1518.  
  1519.     hp = newnode(Node_hashnode);
  1520.     bucket = hashf(name, len, HASHSIZE);
  1521.     hp->hnext = table[bucket];
  1522.     table[bucket] = hp;
  1523.     hp->hlength = len;
  1524.     hp->hvalue = value;
  1525.     emalloc(hp->hname, char *, len + 1, "install");
  1526.     memcpy(hp->hname, name, len);
  1527.     hp->hname[len] = '\0';
  1528.     return hp->hvalue;
  1529. }
  1530.  
  1531. /*
  1532.  * find the most recent hash node for name name (ending with first
  1533.  * non-identifier char) installed by install 
  1534.  */
  1535. NODE *
  1536. lookup(table, name)
  1537. NODE **table;
  1538. char *name;
  1539. {
  1540.     register char *bp;
  1541.     register NODE *bucket;
  1542.     register int len;
  1543.  
  1544.     for (bp = name; is_identchar(*bp); bp++)
  1545.         ;
  1546.     len = bp - name;
  1547.     bucket = table[hashf(name, len, HASHSIZE)];
  1548.     while (bucket) {
  1549.         if (bucket->hlength == len && STREQN(bucket->hname, name, len))
  1550.             return bucket->hvalue;
  1551.         bucket = bucket->hnext;
  1552.     }
  1553.     return NULL;
  1554. }
  1555.  
  1556. #define HASHSTEP(old, c) ((old << 1) + c)
  1557. #define MAKE_POS(v) (v & ~0x80000000)    /* make number positive */
  1558.  
  1559. /*
  1560.  * return hash function on name.
  1561.  */
  1562. static int
  1563. hashf(name, len, hashsize)
  1564. register char *name;
  1565. register int len;
  1566. int hashsize;
  1567. {
  1568.     register int r = 0;
  1569.  
  1570.     while (len--)
  1571.         r = HASHSTEP(r, *name++);
  1572.  
  1573.     r = MAKE_POS(r) % hashsize;
  1574.     return r;
  1575. }
  1576.  
  1577. /*
  1578.  * Add new to the rightmost branch of LIST.  This uses n^2 time, so we make
  1579.  * a simple attempt at optimizing it.
  1580.  */
  1581. static NODE *
  1582. append_right(list, new)
  1583. NODE *list, *new;
  1584.  
  1585. {
  1586.     register NODE *oldlist;
  1587.     static NODE *savefront = NULL, *savetail = NULL;
  1588.  
  1589.     oldlist = list;
  1590.     if (savefront == oldlist) {
  1591.         savetail = savetail->rnode = new;
  1592.         return oldlist;
  1593.     } else
  1594.         savefront = oldlist;
  1595.     while (list->rnode != NULL)
  1596.         list = list->rnode;
  1597.     savetail = list->rnode = new;
  1598.     return oldlist;
  1599. }
  1600.  
  1601. /*
  1602.  * check if name is already installed;  if so, it had better have Null value,
  1603.  * in which case def is added as the value. Otherwise, install name with def
  1604.  * as value. 
  1605.  */
  1606. static void
  1607. func_install(params, def)
  1608. NODE *params;
  1609. NODE *def;
  1610. {
  1611.     NODE *r;
  1612.  
  1613.     pop_params(params->rnode);
  1614.     pop_var(params, 0);
  1615.     r = lookup(variables, params->param);
  1616.     if (r != NULL) {
  1617.         fatal("function name `%s' previously defined", params->param);
  1618.     } else
  1619.         (void) install(variables, params->param,
  1620.             node(params, Node_func, def));
  1621. }
  1622.  
  1623. static void
  1624. pop_var(np, freeit)
  1625. NODE *np;
  1626. int freeit;
  1627. {
  1628.     register char *bp;
  1629.     register NODE *bucket, **save;
  1630.     register int len;
  1631.     char *name;
  1632.  
  1633.     name = np->param;
  1634.     for (bp = name; is_identchar(*bp); bp++)
  1635.         ;
  1636.     len = bp - name;
  1637.     save = &(variables[hashf(name, len, HASHSIZE)]);
  1638.     for (bucket = *save; bucket; bucket = bucket->hnext) {
  1639.         if (len == bucket->hlength && STREQN(bucket->hname, name, len)) {
  1640.             *save = bucket->hnext;
  1641.             freenode(bucket);
  1642.             free(bucket->hname);
  1643.             if (freeit)
  1644.                 free(np->param);
  1645.             return;
  1646.         }
  1647.         save = &(bucket->hnext);
  1648.     }
  1649. }
  1650.  
  1651. static void
  1652. pop_params(params)
  1653. NODE *params;
  1654. {
  1655.     register NODE *np;
  1656.  
  1657.     for (np = params; np != NULL; np = np->rnode)
  1658.         pop_var(np, 1);
  1659. }
  1660.  
  1661. static NODE *
  1662. make_param(name)
  1663. char *name;
  1664. {
  1665.     NODE *r;
  1666.  
  1667.     r = newnode(Node_param_list);
  1668.     r->param = name;
  1669.     r->rnode = NULL;
  1670.     r->param_cnt = param_counter++;
  1671.     return (install(variables, name, r));
  1672. }
  1673.  
  1674. /* Name points to a variable name.  Make sure its in the symbol table */
  1675. NODE *
  1676. variable(name)
  1677. char *name;
  1678. {
  1679.     register NODE *r;
  1680.  
  1681.     if ((r = lookup(variables, name)) == NULL)
  1682.         r = install(variables, name,
  1683.             node(Nnull_string, Node_var, (NODE *) NULL));
  1684.     return r;
  1685. }
  1686.